{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Аггрегация разметки датасета SimpleAr"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Аггрегация строится по следующей системе:\n",
    "\n",
    "1. Сбор размеченных пулов с Толоки. Возможны варианты:\n",
    "    - только общий пул нужно аггрегировать, тогда забирается только он\n",
    "    - часть данных находится в контрольных заданиях и экзамене, тогда к основному пулу добавляются данные задания\n",
    "2. Фильтрация разметчиков:\n",
    "    - в общем пуле есть некоторое количество заранее размеченных заданий - контрольных\n",
    "    - хорошим считается разметчик, который показывает `accuracy >= 0.5` на данных заданиях\n",
    "    - формируется список \"плохих\" разметчиков\n",
    "3. Аггрегация ответов разметчиков по заданиям:\n",
    "    - форматирование в заданиях может отличаться от изначального из-за выгрузки с Толоки\n",
    "    - учитываются только ответы \"хороших\" разметчиков\n",
    "    - аггрегация по подготовленным пулам - создается массив карточек вида {key: value}, где key - кортеж из всех значимых элементов задания, value - список из кортежей вида (user_id, answer)\n",
    "4. Голосование большинством по каждому заданию:\n",
    "    - минимально необходимое большинство составляет 3 голоса, так как такое большинство валидно для перекрытия 5\n",
    "    - по результату формируется датафрейм с заданиями и ответами\n",
    "5. Подгрузка оригинальных данных с разметкой в виде таблицы с заданиями и ответами\n",
    "6. Соединение таблиц:\n",
    "    - очистка форматирования в таблице с ответами разметчиков и в таблице с правильными ответами\n",
    "    - создание единых столбцов с полным заданием\n",
    "    - соединение таблиц по данному столбцу\n",
    "    - валидация размеров\n",
    "7. Подсчет метрик"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "from collections import Counter\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Сбор данных разметки и фильтрация разметчиков"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Данный датасет представляет из себя лишь подмножество, которое было специально сгенерировано. Это не тестовый сет датасета из мультимодального бенчмарка (инструкционный сет)! Размер выборки - 200 объектов."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "assignments = pd.read_csv('assignments_from_pool_41864367__20-10-2023.tsv', sep='\\t')\n",
    "skills = pd.read_csv('workerSkills.csv', sep='|')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Разметчикам предлагалось решить математический пример.\n",
    "\n",
    "Вход: \n",
    "- INPUT:formula (пример: `(2 + (-1))`).\n",
    "\n",
    "Выход:\n",
    "- OUTPUT:result (целое число, например: `1`)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>INPUT:formula</th>\n",
       "      <th>OUTPUT:result</th>\n",
       "      <th>GOLDEN:result</th>\n",
       "      <th>HINT:text</th>\n",
       "      <th>HINT:default_language</th>\n",
       "      <th>ASSIGNMENT:link</th>\n",
       "      <th>ASSIGNMENT:task_id</th>\n",
       "      <th>ASSIGNMENT:assignment_id</th>\n",
       "      <th>ASSIGNMENT:task_suite_id</th>\n",
       "      <th>ASSIGNMENT:worker_id</th>\n",
       "      <th>ASSIGNMENT:status</th>\n",
       "      <th>ASSIGNMENT:started</th>\n",
       "      <th>ASSIGNMENT:submitted</th>\n",
       "      <th>ASSIGNMENT:accepted</th>\n",
       "      <th>ASSIGNMENT:reward</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>8920 + 2043</td>\n",
       "      <td>10963</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>NaN</td>\n",
       "      <td>https://platform.toloka.ai/task/41864367/00027...</td>\n",
       "      <td>00027eccaf--6532b18902a2ad7a848d2d04</td>\n",
       "      <td>00027eccaf--6532b5a51a56d0600c64f94f</td>\n",
       "      <td>00027eccaf--6532b5a51a56d0600c64f94b</td>\n",
       "      <td>4ff3bb526bef2dfc09d25d66edcdc772</td>\n",
       "      <td>APPROVED</td>\n",
       "      <td>2023-10-20T17:15:17.508</td>\n",
       "      <td>2023-10-20T17:15:47.324</td>\n",
       "      <td>2023-10-20T17:15:47.324</td>\n",
       "      <td>0.029</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  INPUT:formula  OUTPUT:result  GOLDEN:result  HINT:text  \\\n",
       "0   8920 + 2043          10963            NaN        NaN   \n",
       "\n",
       "   HINT:default_language                                    ASSIGNMENT:link  \\\n",
       "0                    NaN  https://platform.toloka.ai/task/41864367/00027...   \n",
       "\n",
       "                     ASSIGNMENT:task_id              ASSIGNMENT:assignment_id  \\\n",
       "0  00027eccaf--6532b18902a2ad7a848d2d04  00027eccaf--6532b5a51a56d0600c64f94f   \n",
       "\n",
       "               ASSIGNMENT:task_suite_id              ASSIGNMENT:worker_id  \\\n",
       "0  00027eccaf--6532b5a51a56d0600c64f94b  4ff3bb526bef2dfc09d25d66edcdc772   \n",
       "\n",
       "  ASSIGNMENT:status       ASSIGNMENT:started     ASSIGNMENT:submitted  \\\n",
       "0          APPROVED  2023-10-20T17:15:17.508  2023-10-20T17:15:47.324   \n",
       "\n",
       "       ASSIGNMENT:accepted  ASSIGNMENT:reward  \n",
       "0  2023-10-20T17:15:47.324              0.029  "
      ]
     },
     "execution_count": 3,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "assignments.head(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Фильтруем толокеров с `accuracy < 0.5` на контрольных заданиях, чтобы не учитывать их ответы при подсчете метрик."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Users total:  59\n",
      "Bad users: 1\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "users_dict = defaultdict(lambda: defaultdict(int))\n",
    "\n",
    "for idx, row in assignments.iterrows():\n",
    "    formula = row[0]\n",
    "\n",
    "    out = row[1]\n",
    "    \n",
    "    gold = row[2]\n",
    "\n",
    "    user = row[9]\n",
    "\n",
    "    if str(user) != \"nan\" and str(gold) != \"nan\":\n",
    "        if int(out) == int(gold):\n",
    "            users_dict[user][\"good\"] += 1\n",
    "        else:\n",
    "            users_dict[user][\"bad\"] += 1\n",
    "\n",
    "print(\"Users total: \", len(users_dict))\n",
    "bad_users = []\n",
    "for key, value in users_dict.items():\n",
    "    percentage_good = value[\"good\"]/(value[\"good\"] + value[\"bad\"])\n",
    "    if percentage_good < 0.5:\n",
    "        bad_users.append(key)\n",
    "\n",
    "print(\"Bad users:\", len(bad_users))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "1 из 59 разметчиков на контрольных заданиях показали слишком плохое качество, чтобы учитывать их ответы для расчета метрики."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Отделяем контроль от основы, так как контрольные задания создавались отдельно и не должны учитываться при подсчете метрик. На контрольных заданиях есть `GOLDEN:result`. Также отсеиваем возможные баги Толоки, когда в строке может не быть задания - `INPUT:formula` содержит NaN."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "assignments_no_control = assignments[assignments['GOLDEN:result'].isnull()]\n",
    "assignments_no_control_no_null = assignments_no_control[assignments_no_control['INPUT:formula'].notnull()]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Посчитаем, сколько было затрачено на получение разметки тестовых данных без учета контрольных заданий, так как они могли проходиться неограниченное количество раз одним и тем же разметчиком."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "взвешенная цена айтема в тесте: 0.029\n",
      "потрачено на разметку теста: 28.982\n",
      "28.982 / 0.029\n"
     ]
    }
   ],
   "source": [
    "def w_sum(df):\n",
    "    idx = df.index.values\n",
    "    vals = df.values\n",
    "    summ = idx * vals\n",
    "    return summ.sum()\n",
    "\n",
    "d1 = assignments_no_control_no_null['ASSIGNMENT:reward'].value_counts(normalize=True)\n",
    "d2 = assignments_no_control_no_null['ASSIGNMENT:reward'].value_counts()\n",
    "print(f'взвешенная цена айтема в тесте: {round(w_sum(d1), 3)}')\n",
    "print(f'потрачено на разметку теста: {round(w_sum(d2), 3)}')\n",
    "print(f'{round(w_sum(d2), 3)} / {round(w_sum(d1), 3)}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Выделим, сколько составила средняя часовая ставка для разметки тестовой части датасета. Это будет простое среднее из следующих величин: количество заданий, которое разметчк может сделать за час на основе данного задания, помноженное на цену задания."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "3.3288151569293443"
      ]
     },
     "execution_count": 7,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "def get_hour_pay(df):\n",
    "    try:\n",
    "        times = pd.to_datetime(df['ASSIGNMENT:submitted']) - pd.to_datetime(df['ASSIGNMENT:started'])\n",
    "    except Exception as e:\n",
    "        times = []\n",
    "        for i in range(len(df)):\n",
    "            try:\n",
    "                start = pd.to_datetime(df['ASSIGNMENT:started'].iloc[i])\n",
    "            except Exception as e:\n",
    "                start = pd.to_datetime(df['ASSIGNMENT:started'].apply(lambda x: x.split('T')[1]).iloc[i])\n",
    "            try:\n",
    "                end = pd.to_datetime(df['ASSIGNMENT:submitted'].iloc[i])\n",
    "            except Exception as e:\n",
    "                start = pd.to_datetime(df['ASSIGNMENT:submitted'].apply(lambda x: x.split('T')[1]).iloc[i])\n",
    "            delta = end - start\n",
    "            times.extend([delta])\n",
    "        times = pd.Series(times)\n",
    "        # times = pd.to_datetime(df['ASSIGNMENT:submitted'].apply(lambda x: x.split('T')[1])) - pd.to_datetime(df['ASSIGNMENT:started'].apply(lambda x: x.split('T')[1]))\n",
    "    sums = 3600 / times.apply(lambda x: x.seconds) * df['ASSIGNMENT:reward']\n",
    "    return sums.mean()\n",
    "\n",
    "get_hour_pay(assignments_no_control_no_null)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Сбор ответов разметчиков и голосование"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Собираем ответы голосования большинством для каждого задания."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "200\n"
     ]
    }
   ],
   "source": [
    "from collections import defaultdict\n",
    "\n",
    "text_dict = defaultdict(list)\n",
    "\n",
    "for formula, user, out in zip(\n",
    "    assignments_no_control_no_null[\"INPUT:formula\"], assignments_no_control_no_null[\"ASSIGNMENT:worker_id\"], \n",
    "    assignments_no_control_no_null[\"OUTPUT:result\"]\n",
    "    ):\n",
    "    if user not in bad_users:\n",
    "        text_dict[(formula)].append([\n",
    "                user,\n",
    "                {\"out\": out}\n",
    "        ])\n",
    "\n",
    "print(len(text_dict))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Counter({5: 198, 4: 2})"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "keys = list(text_dict.keys())\n",
    "Counter([len(text_dict[keys[i]]) for i in range(len(keys))])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Только 2 задания с перекрытием 4 человека. Порог согласованности для них устанавливаем такой же, как и для заданий с перекрытием 5 человек - 3 человека для формирования большинства."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full = {}\n",
    "for i in range(len(keys)):\n",
    "    ans = text_dict[keys[i]]\n",
    "    lst = [ans[j][1]['out'] for j in range(len(ans))]\n",
    "    cnt = Counter(lst)\n",
    "    if len(lst) == 5:\n",
    "        most = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][1]\n",
    "        if most >= 3:\n",
    "            res = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][0]\n",
    "            preds_full[keys[i]] = res\n",
    "    elif len(lst) == 4:\n",
    "        most = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][1]\n",
    "        if most > 2:\n",
    "            res = Counter([ans[j][1]['out'] for j in range(len(ans))]).most_common(1)[0][0]\n",
    "            preds_full[keys[i]] = res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "200"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "len(preds_full)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "preds_full_df = pd.concat([pd.DataFrame(preds_full.keys(), columns=['formula']), pd.DataFrame(preds_full.values(), columns=['lb'])], axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Все задания согласованы."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Сопоставление разметки и ground truth"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Забираем задания из датасета с правильными ответами."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df = pd.read_csv('full_wa.tsv', sep='\\t')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [],
   "source": [
    "res_df = res_df.rename({'INPUT:formula': 'formula', 'GOLDEN:result': 'lb'}, axis=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "После скачивания с Толоки в текстах рушится форматирование, потому нельзя просто сделать join двух табличек. Нужно убрать все \"лишнее\" форматирование сразу из двух табличек, чтобы остались только тексты, пунктуация и пробелы."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "def format_text(text):\n",
    "    text = (text.strip().replace('\\n', ' ').replace('\\t', ' ')\n",
    "            .replace('\\r', ' ').replace('  ', ' ').replace('  ', ' ')\n",
    "            .replace('  ', ' '))\n",
    "    return text\n",
    "\n",
    "res_df['formula'] = res_df['formula'].apply(format_text)\n",
    "preds_full_df['formula'] = preds_full_df['formula'].apply(format_text)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Делаем left join, чтобы соединить голосование и правильные метки для одних и тех же заданий."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [],
   "source": [
    "new = res_df.merge(preds_full_df, on='formula', how='left')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "200"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "new_valid = new[new['lb_y'].notna()].copy()\n",
    "len(new_valid)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>formula</th>\n",
       "      <th>lb_x</th>\n",
       "      <th>lb_y</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>377 + 587</td>\n",
       "      <td>964</td>\n",
       "      <td>964</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "     formula  lb_x  lb_y\n",
       "0  377 + 587   964   964"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "new_valid.head(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Подсчет метрики"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Так как в новом датасете есть ровно 200 строк, то мы ничего не потеряли. Мердж табличек прошел корректно и можно посчитать достоверные метрики."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [],
   "source": [
    "new_valid['lb_y'] = new_valid['lb_y'].astype(int)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(new_valid['lb_x'] == new_valid['lb_y']).mean()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Accuracy = 1.0`"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Анализ ошибок"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Доля правильных ответов после голосования большинством составила ровно 1. Это значит, что дальнейший анализ ошибок результатов голосования не имеет смысла. Однако можно посмотреть на отдельные ответы разметчиков, чтобы понять были ли ошибки случайными или нет."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [],
   "source": [
    "from scipy import stats"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "pool = pd.read_csv('assignments_from_pool_41864367__20-10-2023.tsv', sep='\\t')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Убираем из пула контрольные задания."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "pool = pool[pool['GOLDEN:result'].isna()]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [],
   "source": [
    "pool1 = pool.rename({'INPUT:formula': 'formula', 'OUTPUT:result': 'lb'}, axis=1).iloc[:, [0, 1]].copy()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>formula</th>\n",
       "      <th>lb</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>8920 + 2043</td>\n",
       "      <td>10963</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "       formula     lb\n",
       "0  8920 + 2043  10963"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "pool1.head(1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Объединяем основной пул с правильными ответами."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [],
   "source": [
    "pool1['formula'] = pool1['formula'].apply(lambda x: x.strip().replace('\\n', ' ').replace('\\t', ' ').replace('\\r', ' ').replace('  ', ' ').replace('  ', ' ').replace('  ', ' '))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [],
   "source": [
    "new = pool1.merge(res_df, on='formula', how='left')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Смотрим на количество ошибок."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "24"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "(new['lb_x'] != new['lb_y']).sum()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Всего 24 ошибки... Задание слишком простое. С таким количеством ошибок никакой полезной информации извлечь не получится, так как их слишком мало, чтобы утверждать хоть что-то наверняка. Все же попробуем проверить, есть ли хоть какая-то зависимость ошибок от примеров."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "new['cor'] = (new['lb_x'] == new['lb_y']).astype(int)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Имеет смысл оценить сложность примера по размеру слагаемых."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "new['vars'] = new['formula'].apply(lambda x: len(x.split(' + ')[0]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>mean_correct</th>\n",
       "      <th>median_correct</th>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>vars</th>\n",
       "      <th></th>\n",
       "      <th></th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>1.000000</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>0.975862</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>0.978571</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>0.971875</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>0.975000</td>\n",
       "      <td>1.0</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "      mean_correct  median_correct\n",
       "vars                              \n",
       "1         1.000000             1.0\n",
       "2         0.975862             1.0\n",
       "3         0.978571             1.0\n",
       "4         0.971875             1.0\n",
       "5         0.975000             1.0"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "new.groupby('vars').agg(\n",
    "    mean_correct = pd.NamedAgg(column='cor', aggfunc='mean'),\n",
    "    median_correct = pd.NamedAgg(column='cor', aggfunc='median')\n",
    ")"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Нет зависимости между сложностью примера и ошибками. Рассмотрим только ошибки."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "err = new[new['cor'] != 1].copy()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Возьмем разность правильного ответа и ответа разметчика."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [],
   "source": [
    "err['diff'] = err['lb_y'] - err['lb_x']"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "diff\n",
       " 1000     3\n",
       "-100      2\n",
       " 1        2\n",
       "-10       1\n",
       "-1000     1\n",
       " 13500    1\n",
       " 450      1\n",
       " 100      1\n",
       "-13500    1\n",
       " 5        1\n",
       " 12       1\n",
       "-20       1\n",
       " 45       1\n",
       " 8        1\n",
       "-7        1\n",
       " 21       1\n",
       " 81       1\n",
       " 240      1\n",
       " 3        1\n",
       " 97100    1\n",
       "Name: count, dtype: int64"
      ]
     },
     "execution_count": 35,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "err['diff'].value_counts()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Не похоже, что есть хоть какая-то зависимость между ошибками и условиями задач. Ошибок оцень мало, потому энтропии считать нет смысла. В самих ошибках нет никакой системы. Кажется, ошибки произошли целиком и полностью из-за невнимательности либо при ручном прорешивании примеров, либо при вбивании чисел в калькулятор. Отсюда ошибки ровно на 1, 10, 100, 1000 - разница в одну цифру с правильным ответом. Такие разницы (по модулю), как 97100, 13500, 81, 20 также связы с тем, что либо перепутана одна цифра в ответе, либо пропущена одна цифра в ответе, либо добавлена одна лишняя цифра в ответе. Причина всех этих ошибок - невнимательность, которая не связана с текстами заданий."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.4"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
